﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Antlr;
using Antlr.Runtime;
using Antlr.Runtime.Tree;
using System.Collections;
using cudaTranslator;
using MathNet.Numerics.LinearAlgebra.Single;
using cudaMatrix;

namespace CudaTranslator
{
    #region zmienne

    #region variable
    //klasa rodzic wszystkich zmiennych
    class Variable
    {
        //typ i nazwa zmiennej
        private string type, name;

        //konstruktor klasy podstawowej
        public Variable(string _type,string _name) {
            type = _type;
            name=_name;
        }

        //getter dla nazwy zmiennej
        public string GetName() {
            return name;
        }

        //getter dla typu zmiennej
        public string GetVariableType() {
            return type;
        }

        //zwraca wartość dla typu zmiennej
        public virtual Object GetValue(){
        return new Object();
        }
    }

#endregion

    #region functionVariable
    //zmienna trzymajaca informacje o funkcji
    class Function : Variable {

        //referencja na funkcję
        private CommonTree functionDeclaraction;
        //lista zmiennych
        private ArrayList variable_list;

        public Function(string _type, CommonTree _functionDeclaration, string _name,ArrayList _variable_list) : base(_type,_name) {
            functionDeclaraction = _functionDeclaration;
            variable_list=_variable_list;
        }
        
        public ArrayList GetVariables()
        {
            return variable_list;
        }

        public CommonTree GetDeclaration() {
            return functionDeclaraction;
        }

        //zwraca wartość dla typu zmiennej
        public Object GetValue() {
            return functionDeclaraction;
        }


    }

    #endregion

    #region matrixVariable
    //zmienna trzymajaca informacje o macierzy
    class MatrixVariable : Variable {
        private DenseMatrix matrix;
        public MatrixVariable(string _type, string _name, DenseMatrix _matrix)
            : base(_type, _name)
        {
            matrix = _matrix;  //-- macierz może być dowolnego typu
        }

        //zwraca wartość dla typu zmiennej
        public Object GetValue()
        {
            return matrix;
        }

        //setter dla macierzy
        public void SetFloat(DenseMatrix _matrix)
        {
            matrix = _matrix;
        }
    }
    #endregion

    #region integerVariable
    //zmienna trzymajaca informacje o typie całkowitoliczbowym
    class IntegerVariable : Variable {

        //wartość przechowywana w klasie
        private int number;

        //konstruktor korzystający z konstruktora bazowego
        public IntegerVariable(string _type, string _name, int _number) : base(_type,_name){
            number=_number;
        }

        //zwraca wartość dla typu zmiennej
        public override Object GetValue()
        {
            return number ;
        }

        public void SetInt(int _number)
        {
            number = _number;
        }
    }
    #endregion

    #region floatVariable
    //zmienna trzymajaca informacje o typie o pojedynczej prezyzji
    class FloatVariable : Variable { 

        //wartość przechowywana w klasie
        private float number;

        //konstruktor używający konstruktora bazowego
        public FloatVariable(string _type, string _name, float _number) : base(_type,_name){
            number=_number;
        }

        //zwraca wartość dla typu zmiennej

        public override Object GetValue()
        {
            return number;
        }

        //setter
        public void SetFloat(float _number)
        {
            number = _number;
        }
    }
    #endregion

    #region stringVariable
    //zmienna trzymajaca informacje o stringu
    class StringVariable : Variable { 

        //wartosc przechowywana w klasie
        private string text;

        //konstruktor używający konstruktora bazowego
        public StringVariable(string _type, string _name, string _text) : base(_type,_name){
            text=_text;
        }

        //zwraca wartość dla typu zmiennej
        public Object GetValue()
        {
            return text;
        }

        //setter
        public void SetString(string _text)
        {
            text=_text;
        }
    }

    #endregion

    #endregion

    #region treeWalker
    //klasa służaca to analizy wygenerowanego drzewa
    class TreeWalker
    {
        //słownik zmiennych globalnych
        private Dictionary<string, Variable> variablesGlobal = new Dictionary<string, Variable>();   

        //lista scopów
        private LinkedList<Dictionary<string,Variable>> variableScopes=  new LinkedList<Dictionary<string,Variable>>();

        //podstawowe drzewo
        private CommonTree tree;

        //referencja do parsera
        private CudaTranslatorParser parser;

        //konstruktor
        public TreeWalker(CommonTree Tree, CudaTranslatorParser _parser)
        {
            variableScopes.AddFirst(variablesGlobal);
            tree = Tree;
            parser=_parser;
        }

        //główny program analizujący
        public void WalkAndAnalyse(){
           
            /*
            foreach (CommonTree tr in tree.Children) {
                Console.Write(tr.Text+" == "+parser.TokenNames[tr.Type]+"|| ");
            }
            Console.WriteLine("\n\n\n"+tree.ToStringTree());
            */

            int childrenCount=tree.ChildCount;
            if(childrenCount==0){
                return;
            }

             foreach (CommonTree tr in tree.Children)
           {
               //dla deklaracji TYPE jestkorzeniem
               if (parser.TokenNames[tr.Type] == "TYPE") {
                   GetDeclarations(tr);
               }

               //dla funkcji ID jest korzeniem
               else if (parser.TokenNames[tr.Type] == "ID")
               {
                   GetFunction(tr);
               }
                
               //pozostałe
               else {
                   ParseChild(tr);
               }
           }

           //ostatni scope
           Dictionary<string, Variable> variables = variableScopes.Last();
           foreach (string k in variables.Keys) {
               
               if (variables[k].GetVariableType() == "int") {
                   Console.WriteLine(k + " = " + (int)((IntegerVariable)variables[k]).GetValue());
               }
           }
           
       }

       //parsuje glowna czesc programu
       public void GetDeclarations(CommonTree tree){

           //id ostaniej zmiennej
           string lastID = "";
           foreach (CommonTree tr in tree.Children)
           {
               //dopisujemy wartosc
               if (tr.Text == "=") {
                   EvaluateExpression((CommonTree)tr.Children[0],lastID);
               }
               if (parser.TokenNames[tr.Type] == "ID")
               {
                   //sprawdzmy czy int
                   if ( tr.Parent.Text == "int") {
                       lastID = tr.Text;
                       variableScopes.Last().Add(tr.Text, new IntegerVariable("int", "tr.Text", 0));
                   }

                   //sprawdzamy czy matrix
                   if ( tr.Parent.Text == "matrix")
                   {
                       lastID = tr.Text;
                       float[] tmpmat =  {0};
                       variableScopes.Last().Add(tr.Text, new MatrixVariable("matrix", "tr.Text", MatrixFactory.createMatrix(1,1,tmpmat)));
                   }

                   //sprawdzamy czy float
                   if ( tr.Parent.Text == "float")
                   {
                       lastID = tr.Text;
                       variableScopes.Last().Add(tr.Text, new FloatVariable("float", "tr.Text", 0.0f));//jesli nastepny to '='
                   }

                   //sprawdzamy czy string
                   if ( tr.Parent.Text == "string")
                   {
                       lastID = tr.Text;
                       variableScopes.Last().Add(tr.Text, new StringVariable("string", "tr.Text", ""));
                   }
               }

               
           }
       }

           //dodawanie funkcji
       public void GetFunction(CommonTree tree)//wskaznik do poczatku funkcji
       {
           /*
           Console.WriteLine("getting function...");
           //debug
           foreach (CommonTree tr in tree.Children)
           {
               Console.Write(tr.Text + " == " + parser.TokenNames[tr.Type] + "|| ");
           }
           Console.WriteLine("\n\n\n" + tree.ToStringTree());*/
        }

        public void ParseChild(CommonTree tree)//wskaznik do poczatku funkcji
        {
            /*Console.WriteLine("another...");
            //debug
            foreach (CommonTree tr in tree.Children)
            {
                Console.Write(tr.Text + " == " + parser.TokenNames[tr.Type] + "|| ");
            }
            Console.WriteLine("\n\n\n" + tree.ToStringTree());*/
                    }

        //szuka czy istnieje dana zminna
        private Variable FindLastVariable(string id) {
            Variable tmp=null;
            foreach (Dictionary<string, Variable> d in variableScopes)
            {
                if (d.ContainsKey(id)) tmp = d[id];
            }
            if (tmp == null) {
                Console.WriteLine("No such variable: " + id);
            }
                return tmp;
        }

        private void EvaluateExpression(CommonTree tree,string lastID)
        {
            Console.WriteLine(tree.Text);
            if(variableScopes.Last()[lastID].GetVariableType()=="int")
            variableScopes.Last()[lastID] = new IntegerVariable("int", "tr.Text", 23);
        }
    }

    
#endregion
}